﻿#include "utils.h"

/************************************************************
* 读取raw格式图像，要求RGB、RGB格式
* read file(raw type) to mat
************************************************************/
matrix readMat(const imginfo imgif)
{
    printf("reading raw image...\n");
    int height= imgif.h; int width= imgif.w; char channel= imgif.c;
    FILE* fp = fopen(imgif.inpath, "rb");
    if (!fp) {
        printf("\"%s\" read failed!\n", imgif.inpath);
        exit(-1);
        return matrix();
    }
    uchar* p = NULL;
    matrix img;
    int count = height * width * channel;
    img.h = height;
    img.w = width;
    img.channel = channel;
    img.data = p = (uchar*)malloc(sizeof(uchar) * count);
    if (!p) {
        printf("malloc \"p\" failed!\n");
        return matrix();
    }
    fread(p, sizeof(uchar), count, fp);
    fclose(fp);
    printf("read size(p):%d success!\n", (int)sizeof(p));

    return img;
}

/************************************************************
* 写文件到ppm或raw格式
* write to file( ppm or raw)
* support gray image file(.pgm and .raw) and color image file(.ppm and .raw)
************************************************************/
void writeMat(matrix outdata, const char* savepath, uchar pflag) {
    FILE* fp = NULL;
    if (outdata.data == NULL) {
        printf("ERROR:<outdata> is empty!\n ");
        return;
    }
    if (strlen(savepath) < 3) {
        printf("ERROR:<savepath> is too shot!\"%s\"\n ", savepath);
    }
    int i;
    char* tr1 = NULL, * tr2 = NULL;
    char outdir[1024];
    memcpy(outdir, savepath, strlen(savepath));
    outdir[strlen(savepath)] = '\0';
    // char* outdir = (char*)savepath;
    if (pflag == PPM_P6_OUT)
        if (outdata.channel > 1) {
            printf("check ppm(color image) path...\n");
            for (i = 0; i < strlen(savepath); i++) {
                if (savepath[i] == '.') {
                    tr1 = outdir + i + 1;
                    if (strcmp(tr1, "ppm")) {
                        outdir[i + 1] = 'p';
                        outdir[i + 2] = 'p';
                        outdir[i + 3] = 'm';
                        break;
                    }
                }
            }
        }
        else {
            printf("check ppm(gray image and change to \"pgm\" file) path...\n");
            for (i = 0; i < strlen(savepath); i++) {
                if (savepath[i] == '.') {
                    tr1 = outdir + i + 1;
                    if (strcmp(tr1, "pgm")) {
                        outdir[i + 1] = 'p';
                        outdir[i + 2] = 'g';
                        outdir[i + 3] = 'm';
                        break;
                    }
                }
            }
        }
    fopen_s(&fp, outdir, "wb");
    if (fp == NULL) {
        printf("ERROR:can not open file:\"%s\"\n", savepath);
        return;
    }
    switch (pflag)
    {
    case PPM_P6_OUT:
        if (outdata.channel > 1) {
            fprintf(fp, "P6\n%d %d\n%d\n", outdata.w, outdata.h, 255);  //clolor image
        }
        else {
            fprintf(fp, "P5\n%d %d\n%d\n", outdata.w, outdata.h, 255);  //gray image
        }
        fwrite(outdata.data, outdata.channel, outdata.w * outdata.h, fp);
        break;
    case RAW_OUT:
        fwrite(outdata.data, outdata.channel, outdata.w * outdata.h, fp);
        break;
    default:
        fwrite(outdata.data, outdata.channel, outdata.w * outdata.h, fp);
        break;
    }
    fclose(fp);
}

/************************************************************
* 函数名：contain
* 判断str字符串中是否包含字串substr
*  input 2 string : str and substr
*  detect inclusion relationship
*  if str include substr return OK 
*  else return FALSE
************************************************************/
char contain(char* str,char len, char* substr,char sublen) {
    int i,j;
    for (i = 0, j = 0; i < len && j < sublen; i++) {
        while (substr[j] == str[i]&&j<sublen&&i<len) {
            j++; i++;
        }
    }
    if (j == sublen)
        return OK;
    return FALSE;
}

/*************************************************************
* 删除注释部分，支持“/”、“#”的注释
*  split string to substring
*  compatible annotation symbol('/'、'#'、'\n')
*************************************************************/
char splitstr(const char* str,int len, char* str1, char* str2,char flag='\t') {
    int i, l1 = 0, l2 = 0, t = 0;
    if (len < 2|| str[0] == '\n' || str[0] == '#' || str[0] == '/')
        return FALSE;
    for (i = 0; i < len; i++) {
        if (str[i] == '/' || str[i] == '#'||str[i]=='\n')
            break;
        if (str[i] == ':'&&t==0) {
            str1[l1++] = str[i++];
            t=1;
            while (str[i] == ' ' || str[i] == '\t') {
                i++;
            }
        }
        if(t==0)
            str1[l1++] = str[i];
        else
            str2[l2++] = str[i];
    }
    if (l2 == 0) {
        printf("<splitstr> can not find key word: %s\n",str);
        return OK;
    }
    i = l2 - 1;
    while(str2[i] == '\n' || str2[i] == '\t' || str2[i] == ' ') {
        str2[i--] = '\0';
        l2--;
        continue;
    }
    str1[l1] = '\0';
    str2[l2] = '\0';
    return OK;
}

/*************************************************************
* 解析图像相关参数
* Get image information
*************************************************************/
char parseIMG(FILE* fp, para &cfg) {
    if (fp == NULL) {
        printf("input file is \"NULL\"!\n");
        return FALSE;
    }
    int length;
    char s[256] = { 0 }, s1[256] = { 0 }, s2[256] = { 0 };
    printf("parsing of image parameter\n");
    do {
        fgets(s, 256, fp);
        if (s[0] == '/' || s[0] == '#')
            continue;
        if (splitstr(s, strlen(s), s1, s2) == OK) {
            if (0 == strcmp(s1, "input path:")) {
                length = strlen(s2);
                cfg.img.inpath = (char*)malloc(length + 1);
                if (cfg.img.inpath == NULL) {
                    printf("<parseIMG>malloc memory failed!\n");
                    return FALSE;
                }
                memcpy(cfg.img.inpath, s2, length);
                cfg.img.inpath[length] = '\0';
            }
            else
                if (0 == strcmp(s1, "save path:")) {
                    length = strlen(s2);
                    cfg.img.savepath = (char*)malloc(length + 1);
                    if (cfg.img.savepath == NULL) {
                        printf("<parseIMG>malloc memory failed!\n");
                        return FALSE;
                    }
                    memcpy(cfg.img.savepath, s2, length);
                    cfg.img.savepath[length] = '\0';
                }
                else
                    if (0 == strcmp(s1, "image width:")) {
                        int l = atoi(s2);
                        cfg.img.w = l;
                    }
                    else
                        if (0 == strcmp(s1, "image height:")) {
                            int l = atoi(s2);
                            cfg.img.h = l;
                        }
                        else
                            if (0 == strcmp(s1, "image channel:")) {
                                int l = atoi(s2);
                                cfg.img.c = l;
                            }
                            else
                                if (0 == strcmp(s1, "mode:")) {
                                    int l = atoi(s2);
                                    cfg.mode = l;
                                    return OK;
                                }
        }
    } while (feof(fp) == 0 && s[0] != '\n' && s[0] != '[');
    return OK;
}

/**************************************************************
* 解析算子参数
* 兼容两类注释
* Get Gaussion table information
**************************************************************/
char paraseGausstable(FILE* fp, para& cfg) {
    if (fp == NULL) {
        printf("input file is \"NULL\"!\n");
        return FALSE;
    }
    printf("parsing of table gauss parameter\n");
    int length, j, i;
    char s[256] = { 0 }, s1[256] = { 0 }, s2[256] = { 0 };
    do {
        fgets(s, 256, fp);
        if (s[0] == '/' || s[0] == '#' || s[0] == '\n')
            continue;
        if (splitstr(s, strlen(s), s1, s2) == OK) {
            if (0 == strcmp(s1, "table w:")) {
                cfg.gaustable.w = atoi(s2);
            }
            else if (0 == strcmp(s1, "table h:")) {
                cfg.gaustable.h = atoi(s2);
                int t, * pt, index, w = cfg.gaustable.w, h = cfg.gaustable.h, sum = 0;
                length = ((cfg.gaustable.w >> 1) + 1);
                //p = (int*)malloc(sizeof(int) * length);
                cfg.gaustable.d = pt = (int*)malloc(sizeof(int) * w * h);
                if (pt == NULL) {
                    printf("<parseconfig-gauss table>ERROR:can not malloc memory\n");
                    return FALSE;
                }
                for (i = 0; i < length; i++) {
                    for (j = 0; j <= i; j++) {
                        if (fscanf(fp, "%d", &t) == EOF)
                            break;
                        index = w * ((h >> 1) + i) + (w >> 1) + j;
                        pt[index] = t;
                        index = w * ((h >> 1) - i) + (w >> 1) + j;
                        pt[index] = t;
                        index = w * ((h >> 1) + i) + (w >> 1) - j;
                        pt[index] = t;
                        index = w * ((h >> 1) - i) + (w >> 1) - j;
                        pt[index] = t;
                        sum += t << 2;
                        if (i != 0 && (i != j)) {
                            index = w * ((w >> 1) + j) + (h >> 1) + i;
                            pt[index] = t;
                            index = w * ((w >> 1) - j) + (h >> 1) + i;
                            pt[index] = t;
                            index = w * ((w >> 1) + j) + (h >> 1) - i;
                            pt[index] = t;
                            index = w * ((w >> 1) - j) + (h >> 1) - i;
                            pt[index] = t;
                            sum += t << 2;
                        }
                    }
                }
                index = w * ((w >> 1)) + (h >> 1);
                sum -= pt[index] * 3;
                cfg.gaustable.sum = sum;

                printf("Gaussion table:\n");
                for (i = 0; i < cfg.gaustable.h; i++) {
                    for (j = 0; j < cfg.gaustable.w; j++) {
                        printf("%d\t", ((int*)cfg.gaustable.d)[i * cfg.gaustable.w + j]);
                    }
                    printf("\n");
                }
                printf("\n");

                return OK;
            }
        }
    } while (feof(fp) == 0 && s[0] != '\n' && s[0] != '[');
    return OK;
}

char paraseBS(FILE* fp, para& cfg) {

    return OK;
}

/**************************************************************
* 解析canny算法参数
* 
**************************************************************/
char paraseCanny(FILE* fp, para& cfg) {
    if (fp == NULL) {
        printf("input file is \"NULL\"!\n");
        return FALSE;
    }
    char s[256] = { 0 }, s1[256] = { 0 }, s2[256] = { 0 };
    printf("parsing of Canny parameter\n");
    do {
        fgets(s, 256, fp);
        if (s[0] == '/' || s[0] == '#')
            continue;
        if (splitstr(s, strlen(s), s1, s2) == OK) {
            if (0 == strcmp(s1, "double threshold:")) {
                int a, b;
                if (sscanf(s2, "%d %d", &a,&b) == EOF) {
                    printf("<paraseCanny>ERROR! can not read 'double threshold'\n");
                    return FALSE;
                }
                if (a > b) {
                    printf("<paraseCanny>ERROR! %d:'a'(first) more than %d:'b'(second)\n", a, b);
                    return FALSE;
                }
                cfg.cny.dt[0] = a; cfg.cny.dt[1] = b;
            }
            else
                if (0 == strcmp(s1, "angletable length:")) {
                    cfg.cny.atl = atoi(s2);
                }
                else
                    if (0 == strcmp(s1, "angletable:")) {
                        FILE *fp2 = fopen(s2, "r");
                        if (fp2 == NULL) {
                            printf("<paraseCanny>'%s' is not exist or input is error!\n",s2);
                            return FALSE;
                        }
                        int t, index, length,*p=NULL,i;
                        length = cfg.cny.atl;
                        cfg.cny.at =p= (int*)malloc(sizeof(int)* length);
                        if (p == NULL) {
                            printf("<paraseCanny>'angle table' not enough memory!\n");
                            return FALSE;
                        }
                        for (i = 0; i < length; i++) {
                            if (fscanf(fp2, "%d", &t) != EOF) {
                                *(p) = t;
                                p++;
                            }
                            else {
                                printf("angle table length ERROR! read:%d,real:%d\n", length, i + 1);
                                return FALSE;
                            }
                        }

                        return OK;
                    }
        }
    } while (feof(fp) == 0 && s[0] != '\n' && s[0] != '[');

    return OK;
}
/**************************************************************
* 解析参数文件
* parsing of parameter file
* 
**************************************************************/
para parseconfig(const char* inparapath)
{
    para cfg;
    cfg.img.inpath = NULL;
    FILE* fpara;
    fpara = fopen(inparapath,"r");
    if (fpara == NULL) {
        printf("open para file failed!<%s>\n", inparapath);
        return para();
    }
    char s[256] = {0}, s1[256] = { 0 }, s2[256] = { 0 };
    do {
        fgets(s, 256, fpara);
        if (s[0] == '/' || s[0] == '#' || s[0] == '\t'||s[0]=='\n')
            continue;
        if (contain(s, strlen(s), (char*)("[IMAGE]"), 7) == OK) {         //image parameter
            parseIMG(fpara, cfg);
        }
        if (contain(s, strlen(s), (char*)("[TABLE GAUSS]"), 13) == OK) {  //table gaussian parameter
            paraseGausstable(fpara, cfg);
        }
        if (contain(s, strlen(s), (char*)("[BILATERA SOBEL]"), 16) == OK) {//BILATERA SOBEL
            paraseBS(fpara, cfg);
        }
        if (contain(s, strlen(s), (char*)("[CANNY]"), 7) == OK) {        //canny parameter
            paraseCanny(fpara, cfg);
        }
    } while (feof(fpara) == 0);
    fclose(fpara);
    return cfg;
}

char gaussionfilter(matrix inmat, matrix outmat, matrix table){
    if (inmat.w == 0 || inmat.h == 0) {
        printf("<gaussionfilter> input is empty!\n");
        return FALSE;
    }
    if (outmat.w == 0 || outmat.h == 0) {
        printf("<gaussionfilter> save is empty!\n");
        return FALSE;
    }
    if (table.w == 0 || table.h == 0||table.d==NULL) {
        printf("<gaussionfilter> input table is empty!\n");
        return FALSE;
    }
    int r, c, sum=0;
    if (table.sum == 0) {
        for (r = 0; r < table.h; r++)
            for (c = 0; c < table.w; c++)
                sum += ((int*)table.d)[r * table.w + c];
        table.sum = sum;
    }

    return OK;
}
/**************************************************************
* ppm格式转tif格式
* 
**************************************************************/
void ppm2tif()
{
    cout << "转tif文件工具" << endl;
    PathZXC pather;
    std::string inpath, drive, folder, name, evtname,savepath;
    std::cin >> inpath;
    pather.MYPathSplit(inpath, drive, folder, name, evtname);
    while (inpath.length() < 3|| !pather.pathexists(inpath)) {
        std::cout << "输入路径有误，请重新输入！输入0将退出！" << std::endl;
        std::cin >> inpath;
        if (inpath.length() == 1 && inpath[0] == '0') {
            std::cout << "程序退出！\n";
            exit(0);
        }
        pather.MYPathSplit(inpath, drive, folder, name, evtname);
    }        
    auto paths = pather.GetAllFiles(inpath);
    cv::Mat img;
    for (int i = 0; i < paths.size(); i++) {
        pather.MYPathSplit(paths[i], drive, folder, name, evtname);
        savepath = drive + folder + name + ".tif";
        std::cout << "保存在：" << savepath << std::endl;
        img = cv::imread(paths[i]);
        if (img.empty()) {
            cout << "读取失败，自动跳过>>" << paths[i] << endl;
            continue;
        }
        cv::imwrite(savepath, img);
    }

    std::cout << "计算完成\n";
}

/**************************************************************
* 当某位置edge被标记为非0时进行保留原像素值srcimg，仅适用于3通道
* 
**************************************************************/
void addimgs(matrix srcimg, matrix edgemap,matrix outimg) {
    int r, c;
    uchar* psrc = srcimg.data, * pedge = edgemap.data, * pout = outimg.data;
    for (r = 0; r < srcimg.h; r++) {
        for (c = 0; c < srcimg.w; c++) {
            if (*pedge > 0) {
                pout[0] = 0;
                pout[1] = 0;
                pout[2] = 255;
            }
            else {
                pout[0] = psrc[0];
                pout[1] = psrc[1];
                pout[2] = psrc[2];
            }
            pedge++;
            pout += 3;
            psrc += 3;
        }
    }
}


/**************************************************************
* 3通道RGB图像转灰度图像
* Get Grayimg
**************************************************************/
uchar* GetGrayimg0(matrix img) {
    uchar* p = NULL, * head = NULL, * q = NULL;
    int r, c;
    head = p = (uchar*)malloc(img.w * img.h);
    if (head == NULL) {
        printf("<GetGrayimg>ERROR! not enough memory!\n");
        return head;
    }
    q = img.data;
    for (r = 0; r < img.h; r++)
        for (c = 0; c < img.w; c++) {
            *p = int(q[0] * 0.299 + q[1] * 0.587 + q[2] * 0.114); //Y = 0.299 * R + 0.587 * G + 0.114 * B
            q += 3;
            p++;
        }
    return head;
}

void SetGammaTable(uchar *table,int length, double gamma)
{
    uchar* p=table;
    int i;
    double t;
    for (i = 0; i < length; i++) {
        t = (length - 1)*pow(double(double(i)/ (length-1)), gamma);
        *p = (uchar)((int)t);
        p++;
    }
}
